package com.urbane.dto.services.order;

import com.urbane.dto.services.UserInfoDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.CreditCardNumber;
import org.hibernate.validator.constraints.URL;

import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;

// JSR 303 数据校验，常用注解参考 org.hibernate.validator.constraints、jakarta.validation.constraints 包
// 关于自定义注解校验的使用参考 org.hibernate.validator.constraintvalidators.RegexpURLValidator 类
// @Valid：用于简单且不需要分组校验的场景。常和 @RequestBody 注解一起使用。用于开启校验功能。
// @Validated：提供了更强大的功能，包括分组校验和默认分组指定，适用于复杂业务逻辑下的灵活校验需求。

// @Valid：用于触发对方法参数或返回值的校验。它可以应用于控制器、服务层等地方的方法参数。
// @Validated：除了具有与 @Valid 相同的功能外，还支持分组校验和默认分组指定。它可以应用于类级别或方法级别。
// 在类上添加 @Validated 注解本身并不会直接“开启”数据校验；相反，它指定了该类中所有方法的默认校验分组。这意味着当你在这个类的方法参数上使用 @Valid 或 @Validated 时，这些参数将会根据类级别的分组进行校验。
@Getter
@Setter
@Accessors(chain = true)
@Schema(description = "用户基础信息（脱敏）")
public class CreateOrderRequest implements Serializable {
    @Serial
    private static final long serialVersionUID = 234961389859133490L;

    @NotNull(message = "ID 不能为空")
    @Min(value = 1, message = "ID 必须大于 0")
    @Schema(description = "用户唯一标识符", example = "123")
    private Long id;

    @NotBlank(message = "名称不能为空或空白")
    @Size(min = 3, max = 30, message = "名称长度必须介于 3 到 30 个字符之间")
    @Schema(description = "登录账号，用于系统内部识别", example = "zhangsan")
    private String name;

    @Email(message = "电子邮件格式无效")
    @Schema(description = "脱敏后的邮箱地址", example = "z***@example.com")
    private String email;

    @NotBlank(message = "电话号码不能为空")
    @Pattern(regexp = "\\d{3}-\\d{3}-\\d{4}", message = "电话号码必须采用以下格式 ###-###-####")
    @Schema(description = "用户唯一标识符", example = "123")
    private String phoneNumber;

    @Min(value = 18, message = "年龄必须年满 18 岁")
    @Max(value = 100, message = "年龄不能超过 100 岁")
    @Schema(description = "用户唯一标识符", example = "123")
    private Integer age;

    @Positive(message = "数量必须为正数")
    @Schema(description = "用户唯一标识符", example = "123")
    private int quantity;

    @DecimalMin(value = "10.0", message = "价格必须至少为 10.0 元")
    @DecimalMax(value = "100.0", message = "价格不能超过 100.0 元")
    @Schema(description = "用户唯一标识符", example = "123")
    private BigDecimal price;

    @Past(message = "出生日期必须为过去日期")
    @Schema(description = "用户唯一标识符", example = "123")
    private LocalDate birthdate;

    @Future(message = "日期必须为未来日期")
    @Schema(description = "用户唯一标识符", example = "123")
    private LocalDate futureDate;

    // 允许等于当前时间
    @PastOrPresent(message = "日期必须为过去或今天")
    @Schema(description = "用户唯一标识符", example = "123")
    private LocalDate pastOrPresentDate;

    @FutureOrPresent(message = "日期必须为今天或未来日期")
    @Schema(description = "用户唯一标识符", example = "123")
    private LocalDate futureOrPresentDate;

    @URL(message = "URL 格式无效")
    @Schema(description = "用户唯一标识符", example = "123")
    private String website;

    @CreditCardNumber(message = "无效的信用卡号格式")
    @Schema(description = "用户唯一标识符", example = "123")
    private String creditCardNumber;

    @NotNull(message = "性别不能为空")
    @Schema(description = "用户唯一标识符", example = "123")
    private UserInfoDTO.Gender gender;

    public enum Gender {
        MALE, FEMALE, OTHER
    }
}

// DTO（Data Transfer Object）是一种输入型数据对象，用于在应用程序中传递数据。常作为参数传递。
// 关于数据校验的使用参考 SpringBoot 官方文档： https://docs.spring.io/spring-boot/reference/io/validation.html
// 控制器中的使用：通过 @Valid（方法参数实体前）或 @Validated（类上）注解可以自动验证对象，并在验证失败时捕获错误信息。


// Spring Boot 支持本地化消息，因此您的应用程序可以满足不同语言偏好的用户。默认情况下，Spring Boot 会在类路径的根目录下查找是否存在消息资源包。
// 自动配置适用于已配置资源包的默认属性文件（默认为 messages.properties）。您可以通过在 application.properties 文件中设置 spring.messages.basename 属性来指定消息资源包的名称。

// JSR-303 数据校验文字国际化的配置参考官方文档： https://docs.spring.io/spring-boot/reference/io/validation.html#io.validation.localization
